pwa 入门实践-Service Workers

Service Workers

定义

Service Workers 主要是通过对本网站中所有的HTTP请求进行拦截,并对期进行缓存控制或者响应控制。

引入

Service Worker 其实就是后台线程中运行的javascript文件,所以引入的方式与其他js文件相同

eg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<html>
<head>The best web page ever</head>
<body>
<script>
// 注册 service worker
if ("serviceWorker" in navigator) {
navigator.serviceWorker
.register("/sw.js")
.then(function(registration) {
// 注册成功
console.log(
"ServiceWorker registration successful with scope: ",
registration.scope
);
})
.catch(function(err) {
// 注册失败 :(
console.log("ServiceWorker registration failed: ", err);
});
}
</script>
</body>
</html>

  • 检查当前浏览器是否支持 Service Workers
  • 如果支持,注册一个叫做 ‘sw.js’ 的 Service Worker 文件
  • 如果成功则打印到控制台
  • 如果发生错误,捕获错误并打印到控制台

然后我们在注册的sw.js文件中添加一下内容

1
2
3
4
5
self.addEventListener('fetch', function(event) {     
if (/\.jpg$/.test(event.request.url)) {
event.respondWith(fetch('/images/unicorn.jpg'));
}
});

  • 为 fetch 事件添加事件监听器
  • 检查传入的 HTTP 请求是否是 JPEG 类型的图片
  • 尝试获取独角兽的图片并用它作为替代图片来响应请求

以上我们就成功的在浏览器中通过注册Service Workers的方式代理了本网站的所有请求。并选择我们需要控制的请求,返回特定的响应结果

> 需要注意的是,使用Service Worker 技术一定要https协议才可以,主要是怕被恶意注册非法Service Worker使网站全部请求被劫持。不过本地调试的时候访问localhost不受限制

控制缓存

预缓存

我们可以再 Service Workers 加载的时候,缓存你想要的资源。即用户首次访问本网站, Service Workers会下载并安装自己,我们可以再这个过程中将需要的内容(例如:常用的静态资源)加载好。

eg:

1
2
3
4
5
6
7
8
9
10
var cacheName = 'helloWorld';                
self.addEventListener('install', event => {
event.waitUntil(
caches.open(cacheName)
.then(cache => cache.addAll([
'/js/script.js',
'/images/hello.png'
]))
);
});

  • 首先为缓存创建一个名称cacheName(方便以后对缓存进行精准控制)
  • 进入Service Workers的安装事件中
  • 使用我们指定的缓存名称来打开缓存
  • 把需要的js文件和图片文件添加到缓存中

需要注意的是:在缓存文件下载的过程中,任何文件下载失败都会导致安装过程的失败,使得Servicer Worker安装失败

监听缓存

让 Service Worker 开始监听 fetch 事件,如果遇到了匹配的的资源请求,则自动从缓存中获取内容

1
2
3
4
5
6
7
8
9
10
self.addEventListener("fetch", function(event) {
event.respondWith(
caches.match(event.request).then(function(response) {
if (response) {
return response;
}
return fetch(event.request);
})
);
});
  • 添加 fetch 事件的事件监听器
  • 检查传入的请求 URL 是否匹配当前缓存中存在的任何内容
  • 如果有 response 并且它不是 undefined 或 null 的话就将它返回
  • 否则只是如往常一样继续,通过网络获取预期的资源

    拦截请求同时缓存

    一个请求被捕获的时候,首先检查请求的资源是否存在于缓存之中。如果存在,我们可以就此返回缓存并不再继续执行代码。

如果请求的资源于缓存之中没有的话,我们就重新发起网络请求。然后,我们需要检查 HTTP 响应,确保服务器返回的是成功响应并且没有任何问题。

如果成功响应,我们会再次克隆响应。你可能会疑惑我们为什么需要再次克隆响应

最后,将这个这个响应的结果添加至缓存中,以便下次再使用它。如果用户刷新页面或访问网站另一个请求了这些资源的页面,它会立即从缓存中获取资源。

eg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var cacheName = "helloWorld";
self.addEventListener("fetch", function(event) {
event.respondWith(
caches.match(event.request).then(function(response) {
if (response) {
return response;
}
var requestToCache = event.request.clone();
return fetch(requestToCache).then(function(response) {
if (!response || response.status !== 200) {
return response;
}
var responseToCache = response.clone();
caches.open(cacheName).then(function(cache) {
cache.put(requestToCache, responseToCache);
});
return response;
});
})
);
});
  • 自定义缓存的名称
  • 为 fetch 事件添加事件监听器以拦截请求
  • 当前请求是否匹配缓存中存在的任何内容?
  • 如果匹配的话,就此返回缓存并不再继续执行
  • 这很重要,我们克隆了请求。请求是一个流,只能消耗一次。
  • 尝试按预期一样发起原始的 HTTP 请求
  • 如果由于任何原因请求失败或者服务器响应了错误代码,则立即返回错误信息
  • 再一次,我们需要克隆响应,因为我们需要将其添加到缓存中,而且它还将用于最终返回响应
  • 打开名称为 “helloWorld” 的缓存
  • 将响应添加到缓存中

处理额外的查询参数

当 Service Worker 检查已缓存的响应时,它使用请求 URL 作为键。默认情况下,请求 URL 必须与用于存储已缓存响应的 URL 完全匹配,包括 URL 查询部分的任何字符串。

如果对文件发起的 HTTP 请求附带了任意查询字符串,并且查询字符串会更改,这可能会导致一些问题。例如,如果你对一个先前匹配的 URL 发起了请求,则可能会发现由于查询字符串略有不同而导致该 URL 找不到。当检查缓存时想要忽略查询字符串,使用 ignoreSearch 属性并设置为 true 。

eg:

1
2
3
4
5
6
7
8
9
self.addEventListener('fetch', function (event) {
event.respondWith(
caches.match(event.request, {
ignoreSearch: true
}).then(function (response) {
return response || fetch(event.request);
})
);
});

其他选项:

  • ignoreMethod:忽略请求方法
  • ignoreVary:忽略请求中的vary首部